package com.android.webserver.tornado;

import java.net.*;
import java.io.*;
import java.util.ArrayList;
import java.util.Date;

import com.android.webserver.WebAppDispatcher;

import android.util.Log;

public class ServerThread extends Thread
{

	// per-client resources: these are recreated for every connection
	private Request									request;
	private Response								response;
	/** The message in the access log for this request. */
	private CommonLogMessage				accessLog;

	// per-server resources: these are created once per ServerThread
	private final ArrayList<Socket>	taskPool;
	private final ServerPool				serverPool;

	/**
	 * Constructs a new ServerThread using the specified values. This should only
	 * rarely be called directly: for most purposes, you should spawn new threads
	 * using {@link tornado.ServerPool#addThread()}.
	 */
	ServerThread(ThreadGroup group, ArrayList<Socket> tPool, ServerPool sPool)
	{
		super(group, "");
		taskPool = tPool;
		serverPool = sPool;
	}

	/** Begins an infinite loop waiting for connections and serving them. */
	public void run()
	{
		Socket socket;
		while (true)
		{
			synchronized (taskPool)
			{
				/*
				 * Wait until we find an incoming connection. If pool is non-empty,
				 * there is already a connection waiting to be processed, so we can skip
				 * the wait().
				 */
				while (taskPool.isEmpty())
				{
					try
					{
						taskPool.wait();
					}
					catch (InterruptedException e)
					{
						/*
						 * We were interrupted by another thread. In the current design,
						 * this means the ServerPool wants us to die.
						 */
						return;
					}
				}
				// finally, we have an incoming connection
				socket = (Socket) taskPool.remove(0);
			}

			// start the HTTP transaction with the client
			try
			{

				request = new Request(socket);
				Log.i("WebServer", new Date() + " : " + request.getRawRequest());
				response = new Response(request);
				request.readRequest();
				
				accessLog = new CommonLogMessage(request);
				handOffRequest();
				Log.i("WebServer", new Date() + " : " + request.getRawRequest()+  " : " + response.HTTPStatusCode);
			}
			catch (HTTPException e)
			{
				// we got a protocol error of some kind - expected
				Log.e("WebServer", request.getRequestURL() +  " : HTTPException " + "("+ response);
				sendErrorPage(e);
			}
			catch (Exception e)
			{
				// we got a more serious error - these should not occur
				final Writer result = new StringWriter();
				final PrintWriter printWriter = new PrintWriter(result);
				e.printStackTrace(printWriter);
				if (request == null)
					Log.e("WebServer",  "Request NULL");
				else if (result == null)
					Log.e("WebServer",  "result NULL");
				else
				Log.e("WebServer",  request.getRequestURL() + "Exception " + result.toString());				
			}
			finally
			{
//				Tornado.log.logAccess(accessLog);
				finishConnection();
			}
		}
	}

	/**
	 * Decides what to do with a connection. This looks at the HTTP headers sent
	 * by the client, sends error pages as necessary, and then decides which
	 * method to use to actually handle this request.
	 */
	private void handOffRequest() throws HTTPException
	{
		String method = request.getType();

		if (method == null)
		{
			return;
		}
		else
			if (method.equals("GET"))
			{
				handleGetRequest();
			}
			else
				if (method.equals("HEAD"))
				{
					handleHeadRequest();
				}
				else
					if (method.equals("POST"))
					{
						handlePostRequest();
					}
					else
						if (method.equals("PUT"))
						{
							handlePutRequest();
						}
						else
							if (method.equals("OPTIONS"))
							{
								handleOptionsRequest();
							}
							else
								if (method.equals("DELETE"))
								{
									handleDeleteRequest();
								}
								else
									if (method.equals("TRACE"))
									{
										handleTraceRequest();
									}
									else
										if (method.equals("CONNECT"))
										{
											handleConnectRequest();
										}
										else
										{
											throw new HTTPException(HTTP.NOT_IMPLEMENTED);
										}
	}

	/** Handles an HTTP GET request from the client. */
	private void handleGetRequest() throws HTTPException
	{
		if (!WebAppDispatcher.dispatch(Tornado.androidService, request, response))
			throw new HTTPException(HTTP.NOT_FOUND);

		try
		{
			sendStatus(response.HTTPStatusCode);
			response.sendHeaders();
			response.sendContent();
			response.rawOut.flush();
		}
		catch (IOException e)
		{
			e.printStackTrace();
		}
	}

	/** Handles an HTTP HEAD request from the client. */
	private void handleHeadRequest() throws HTTPException
	{
		if (!WebAppDispatcher.dispatch(Tornado.androidService, request, response))
			throw new HTTPException(HTTP.NOT_FOUND);

		try
		{
			sendStatus(response.HTTPStatusCode);
			response.sendHeaders();
		}
		catch (IOException e)
		{
			e.printStackTrace();
		}
	}

	/**
	 * Handles an HTTP POST request from the client. Not implemented yet.
	 */
	private void handlePostRequest() throws HTTPException
	{
		if (!WebAppDispatcher.dispatch(Tornado.androidService, request, response))
			throw new HTTPException(HTTP.NOT_FOUND);

		try
		{
			sendStatus(response.HTTPStatusCode);
			response.sendHeaders();
			response.sendContent();
			response.rawOut.flush();
		}
		catch (IOException e)
		{
			e.printStackTrace();
		}
	}

	/**
	 * Handles an HTTP PUT request from the client. Not implemented yet.
	 */
	private void handlePutRequest() throws HTTPException
	{
		throw new HTTPException(HTTP.NOT_IMPLEMENTED);
	}

	/**
	 * Handles an HTTP OPTIONS request from the client. Not implemented yet.
	 */
	private void handleOptionsRequest() throws HTTPException
	{
		throw new HTTPException(HTTP.NOT_IMPLEMENTED);
	}

	/**
	 * Handles an HTTP DELETE request from the client. Not implemented yet.
	 */
	private void handleDeleteRequest() throws HTTPException
	{
		throw new HTTPException(HTTP.NOT_IMPLEMENTED);
	}

	/** Handles an HTTP TRACE request from the client. */
	private void handleTraceRequest() throws HTTPException
	{
		throw new HTTPException(HTTP.NOT_IMPLEMENTED);
	}

	/**
	 * Handles an HTTP CONNECT request from the client. Not implemented yet.
	 */
	private void handleConnectRequest() throws HTTPException
	{
		throw new HTTPException(HTTP.NOT_IMPLEMENTED);
	}

	/** Concludes the work with the client and informs the ServerPool. */
	private void finishConnection()
	{
		if (response != null)
		{
			try
			{
				response.finishResponse();
			}
			catch (IOException e)
			{
				e.printStackTrace();
			}
		}
		serverPool.decrementBusyThreads();
	}

	/** Sends the specified HTTP error code page to the client. */
	private void sendErrorPage(HTTPException httpE)
	{
		try
		{
			sendStatus(httpE.getCode());
			response.addHeader("Content-Type: text/html");
//			response.headers = "Content-Type: text/html\r\n";
			response.sendHeaders();
			
			response.out.write(httpE.getErrorPage());
		}
		catch (IOException e)
		{
			e.printStackTrace();
		}
	}

	/**
	 * Sends the specified HTTP status code to the client. This is just a simple
	 * wrapper over {@link tornado.Response#sendStatus(int)}, with one additional
	 * function: it notes the status code in the access log before sending it.
	 */
	private void sendStatus(int statusCode) throws IOException
	{
		if (accessLog != null)
		accessLog.setStatusCode(statusCode);
		response.sendStatus(statusCode);
	}

}
